home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Resources / Browsers, Managers & Extensions / Foxmarks Bookmark Synchronizer 2.5 / foxmarks_bookmark_synchronizer-2.5.0-fx.xpi / components / foxmarks-service.js next >
Text File  |  2008-10-14  |  34KB  |  1,057 lines

  1. /*
  2.  Copyright 2005-2008 Foxmarks Inc.
  3.  
  4.  foxmarks-service.js: component that implements the "service" interface to
  5.  the core synchronization code.
  6.  
  7.  */
  8.  
  9. var Cc = Components.classes;
  10. var Ci = Components.interfaces;
  11.  
  12. function LoadJavascript(filename, id) {
  13.     if (id == "undefined") {
  14.         Cc["@mozilla.org/moz/jssubscript-loader;1"].
  15.         getService(Ci.mozIJSSubScriptLoader).
  16.         loadSubScript(filename, null);
  17.     }
  18. }
  19.  
  20. function GetTopWin(wintype) {
  21.     var topwin = Cc['@mozilla.org/appshell/window-mediator;1'].
  22.         getService(Ci.nsIWindowMediator).
  23.         getMostRecentWindow(wintype);
  24.  
  25.     return topwin;
  26. }
  27.  
  28.  
  29. function FoxmarksLaunchUpgradePage() {
  30.     upgradeCallback.timer = Cc["@mozilla.org/timer;1"].
  31.         createInstance(Ci.nsITimer);
  32.     upgradeCallback.timer.initWithCallback(upgradeCallback, 5000,
  33.         Ci.nsITimer.TYPE_ONE_SHOT);
  34. }
  35.  
  36. function FoxmarksBuildPostData(num_bookmarks){
  37.     return {
  38.         username: gSettings.username,
  39.         version: FoxmarksVersion(),
  40.         types: {
  41.             bookmarks: {
  42.                 is_enabled: gSettings.isSyncEnabled("bookmarks"),
  43.                 count: num_bookmarks
  44.             },
  45.             passwords: {
  46.                 is_enabled: gSettings.isSyncEnabled("passwords")
  47.             }
  48.         }
  49.     };
  50. }
  51. var upgradeCallback = {
  52.     notify: function(timer) {
  53.         fms.server.countItems("bookmarks",
  54.             "bookmark", function(status, num_bookmarks){
  55.             var currver = FoxmarksVersion();
  56.             var wm = Cc['@mozilla.org/appshell/window-mediator;1'].
  57.                 getService(Ci.nsIWindowMediator);
  58.             var topWindow = wm.getMostRecentWindow('navigator:browser');
  59.  
  60.             if(topWindow &&
  61.                 topWindow.document){ 
  62.                 var appcontent =
  63.                     topWindow.document.getElementById("appcontent");
  64.                 var listener = function(e){
  65.                     var doc = e.originalTarget;
  66.                     var button = 
  67.                         doc.getElementById(
  68.                         "set_up_password_sync_button"
  69.                     );
  70.  
  71.                     if(button){
  72.                         button.setAttribute("onclick",  null);
  73.                         button.addEventListener(
  74.                             "click",
  75.                             function(){
  76.                                 var os = Cc["@mozilla.org/observer-service;1"].
  77.                                     getService(Ci.nsIObserverService);
  78.                                 os.notifyObservers(null, 
  79.                                     "foxmarks-showsettingpane", 
  80.                                     "foxmarks-syncpane"
  81.                                 );
  82.                             }, 
  83.                             true
  84.                         );
  85.                         appcontent.removeEventListener(
  86.                             "DOMContentLoaded",
  87.                             listener, 
  88.                             false
  89.                         );
  90.                     }
  91.                 };
  92.                 if(appcontent){
  93.                     appcontent.addEventListener(
  94.                         "DOMContentLoaded",
  95.                         listener, 
  96.                         false
  97.                     );
  98.                 }
  99.             }
  100.             FoxmarksOpenInNewTab(gSettings.httpProtocol + gSettings.webHost + "/firefox/upgrade/" + currver, 
  101.                 true, FoxmarksBuildPostData(num_bookmarks));
  102.             gSettings.currVersion = currver;
  103.         });
  104.     }
  105. }
  106. function FoxmarksLaunchSetupWizard() {
  107.     wizardCallback.timer = Cc["@mozilla.org/timer;1"].
  108.         createInstance(Ci.nsITimer);
  109.     wizardCallback.timer.initWithCallback(wizardCallback, 5000,
  110.         Ci.nsITimer.TYPE_ONE_SHOT);
  111. }
  112.  
  113. var wizardCallback = {
  114.     notify: function(timer) {
  115.         FoxmarksOpenWizard(false, gSettings.username != "");
  116.     }
  117. }
  118.  
  119. function HandleShutdown(cancel) {
  120.     var retval = { helpurl: null };
  121.     var sb = Bundle().GetStringFromName;
  122.     var dontask = {value: false};
  123.     var rv = 0;
  124.  
  125.     if (!gSettings.haveSynced || GetState() != "dirty") {
  126.         return;
  127.     }
  128.  
  129.     var topwin = GetTopWin();
  130.  
  131.     if (!topwin) {
  132.         LogWrite("HandleShutdown: Couldn't find a topwin!");
  133.         return;
  134.     }
  135.  
  136.     if (gSettings.syncOnShutdown && gSettings.syncOnShutdownAsk) {
  137.  
  138.         rv = Cc["@mozilla.org/embedcomp/prompt-service;1"].
  139.         getService(Ci.nsIPromptService).
  140.         confirmEx(topwin, sb("appname.long"), sb("msg.unsynced"),
  141.             Ci.nsIPromptService.STD_YES_NO_BUTTONS, null, null, null,
  142.             sb("msg.dontask"), dontask);
  143.         // Reverse sense: confirmEx returns 0 - yes, 1 - no
  144.         rv = !rv;
  145.  
  146.         // If user says "don't ask me again", set syncOnShutdown to whatever
  147.         // they have chosen in this instance.
  148.         if (dontask.value) {
  149.             gSettings.syncOnShutdown = rv;
  150.         }
  151.         gSettings.syncOnShutdownAsk = !dontask.value;
  152.  
  153.     } else {                           // don't ask
  154.         rv = gSettings.syncOnShutdown;
  155.     }
  156.  
  157.     if (rv) {
  158.         var win = topwin.openDialog(
  159.             "chrome://foxmarks/content/foxmarks-progress.xul", "_blank",
  160.             "chrome,dialog,modal,centerscreen", "synch", retval, null);
  161.         if (retval.helpurl) { // we hit an error and user pressed help button
  162.             if (cancel instanceof Ci.nsISupportsPRBoolean) {
  163.                 cancel.value = true;
  164.                 topwin.openDialog("chrome://browser/content/browser.xul",
  165.                     "_blank", "chrome,all,dialog=no", retval.helpurl);
  166.             }
  167.         }
  168.     }
  169. }
  170.  
  171. function LoadFiles() {
  172.     LoadJavascript("chrome://foxmarks/content/foxmarks-log.js",
  173.         typeof(LogWrite));
  174.     LoadJavascript("chrome://foxmarks/content/foxmarks-settings.js",
  175.         typeof(gSettings));
  176.     LoadJavascript("chrome://foxmarks/content/foxmarks-update.js",
  177.         typeof(ForceUpdate));
  178.     LoadJavascript("chrome://foxmarks/content/foxmarks-clobber.js",
  179.         typeof(onClobberCancel));
  180.     LoadJavascript("chrome://foxmarks/content/foxmarks-bookmark.js",
  181.         typeof(loadDatasourceSet));
  182.     if ("@mozilla.org/browser/nav-bookmarks-service;1" in Cc)
  183.         LoadJavascript("chrome://foxmarks/content/foxmarks-places.js",
  184.             typeof(BookmarkDatasource));
  185.     else
  186.         LoadJavascript("chrome://foxmarks/content/foxmarks-rdf.js",
  187.             typeof(BookmarkDatasource));
  188.     LoadJavascript("chrome://foxmarks/content/foxmarks-nodes.js",
  189.         typeof(Node));
  190.     LoadJavascript("chrome://foxmarks/content/foxmarks-command.js",
  191.         typeof(Command));
  192.     LoadJavascript("chrome://foxmarks/content/foxmarks-core.js",
  193.         typeof(Synchronize));
  194.     LoadJavascript("chrome://foxmarks/content/foxmarks-network.js",
  195.         typeof(Request));
  196.     LoadJavascript("chrome://foxmarks/content/foxmarks-json.js",
  197.         "undefined");
  198.  
  199.     LoadJavascript("chrome://foxmarks/content/shared/Base64.js",
  200.         typeof(Base64));
  201.     LoadJavascript("chrome://foxmarks/content/shared/CreateAESManager.js",
  202.         typeof(CreateAESManager));
  203.     LoadJavascript("chrome://foxmarks/content/foxmarks-utils.js",
  204.         typeof(forEach));
  205.     LoadJavascript("chrome://foxmarks/content/foxmarks-unittest.js",
  206.         typeof(gFoxmarksUT));
  207.     LoadJavascript("chrome://foxmarks/content/foxmarks-uitools.js",
  208.         typeof(FoxmarksOpenWindowByType));
  209.     if("@mozilla.org/login-manager;1" in Cc){
  210.         LoadJavascript("chrome://foxmarks/content/foxmarks-password.js",
  211.             typeof(PasswordDatasource));
  212.     }
  213.     LoadJavascript("chrome://foxmarks/content/foxmarks-server.js",
  214.         typeof(SyncServer));
  215. }
  216.  
  217. var logStream = null;
  218.  
  219. function removeTempLogFile(){
  220.     var fileremoved = Cc['@mozilla.org/file/directory_service;1']
  221.         .getService(Ci.nsIProperties)
  222.         .get('ProfD', Ci.nsIFile);
  223.     fileremoved.append("foxmarks.temp.log");
  224.     try {
  225.         fileremoved.remove(false);
  226.     } catch(e){}
  227. }
  228. function logMoveFile(){
  229.     try {
  230.         var file = Cc['@mozilla.org/file/directory_service;1']
  231.             .getService(Ci.nsIProperties)
  232.             .get('ProfD', Ci.nsIFile);
  233.         file.append("foxmarks.log");
  234.  
  235.         var dir = Cc['@mozilla.org/file/directory_service;1']
  236.             .getService(Ci.nsIProperties)
  237.             .get('ProfD', Ci.nsIFile);
  238.         removeTempLogFile();
  239.         file.moveTo(dir, "foxmarks.temp.log");
  240.  
  241.  
  242.         var fromstream = Cc["@mozilla.org/network/file-input-stream;1"]
  243.             .createInstance(Ci.nsIFileInputStream);
  244.         var tostream = Cc["@mozilla.org/network/file-output-stream;1"]
  245.             .createInstance(Ci.nsIFileOutputStream);
  246.  
  247.         fromstream.init(file, -1, 0x01, 0);
  248.         var logSeek = fromstream.QueryInterface(Ci.nsISeekableStream);
  249.         var lread = fromstream.QueryInterface(Ci.nsILineInputStream);
  250.         var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
  251.             .createInstance(Ci.nsIScriptableUnicodeConverter);
  252.         converter.charset = "UTF-8";
  253.         var i =  100 * 1024;
  254.         if(i > file.fileSize){
  255.             i = file.fileSize;
  256.         }
  257.  
  258.         var filenew = Cc['@mozilla.org/file/directory_service;1']
  259.             .getService(Ci.nsIProperties)
  260.             .get('ProfD', Ci.nsIFile);
  261.         filenew.append("foxmarks.log");
  262.         tostream.init(filenew, 0x02 | 0x08 | 0x10, 0664, 0);
  263.  
  264.         logSeek.seek(logSeek.NS_SEEK_END, -i);
  265.  
  266.         var buf;
  267.         var cont = true;
  268.         var lineData = {};
  269.         var ctr = 0;
  270.         // throw out the first one; could be mid line
  271.         cont = lread.readLine(lineData);
  272.         while(cont){
  273.             lineData = {};
  274.             cont = lread.readLine(lineData);
  275.             buf = converter.ConvertToUnicode(lineData.value) + "\n";
  276.             tostream.write(buf, buf.length);
  277.         }
  278.  
  279.      } catch(e){
  280.         Components.utils.reportError(e);
  281.      } finally {
  282.         if(fromstream !== undefined)
  283.             fromstream.close();
  284.         if(tostream !== undefined)
  285.             tostream.close();
  286.      }
  287.     removeTempLogFile();
  288. }
  289. function logFileOpen() {
  290.     var file = Cc['@mozilla.org/file/directory_service;1']
  291.     .getService(Ci.nsIProperties)
  292.     .get('ProfD', Ci.nsIFile);
  293.  
  294.     var needsTruncate = false;
  295.     var filesize = 0;
  296.     file.append("foxmarks.log");
  297.  
  298.     // check the file size
  299.     try {
  300.        if(file.isFile()){
  301.             filesize = file.fileSize;
  302.        }
  303.  
  304.        if(filesize > 500 * 1024 && gSettings.truncateLog ){
  305.             logMoveFile();
  306.        }
  307.     } catch(e) {
  308.         Components.utils.reportError(e);
  309.     }
  310.     
  311.     try {
  312.         logStream = Cc["@mozilla.org/network/file-output-stream;1"]
  313.         .createInstance(Ci.nsIFileOutputStream);
  314.  
  315.         // use write, append, create
  316.         logStream.init(file, 0x02 | 0x08 | 0x10, 0664, 0);
  317.     } catch (e) {
  318.         // We failed to open. Close and try again next time.
  319.         logFileClose();
  320.     }
  321. }
  322.  
  323. function logFileClose() {
  324.     try {
  325.         logStream.close();
  326.     } catch (e) {}
  327.     logStream = null;
  328. }
  329.  
  330. var gFailureCount = 0;// number of consecutive times we've failed
  331. var gBackoffUntil = 0;// if we've been failing, our backoff time (ms since 1970)
  332. var gServerBackoff = 
  333.     { 'bookmarks': 0,
  334.       'passwords': 0,
  335.       min: function(){
  336.         return Math.min(this['bookmarks'], this['passwords']);
  337.       },
  338.       max: function(){
  339.         return Math.max(this['bookmarks'], this['passwords']);
  340.       },
  341.       clear: function(){
  342.         this['bookmarks'] = 0;
  343.         this['passwords'] = 0;
  344.       }
  345.     };  // seconds server wants us to back off
  346.  
  347. function ReturnErrorMsg(code, msg, restoreState) {
  348.     gSettings.lastError  = code;
  349.     Notify({status: code, msg: msg });
  350.     ClearBusy();
  351.     SetState(restoreState ? restoreState : ((code == 503 || code == 2)
  352.             ? "dirty" : "error"));
  353.     LogWrite("Returned error: " + msg + "(" + code + ")");
  354.     if(code != 2){
  355.         var d = new Date();
  356.         // Initial back-off is 15 minutes, doubling with each error
  357.         gBackoffUntil = d.getTime() + 1000 * (Math.max(
  358.             15 * 60 * Math.pow(2, gFailureCount++),
  359.             gServerBackoff.max()) + Math.floor(Math.random() * 15));
  360.         gServerBackoff.clear();;
  361.         var retry = new Date();
  362.         retry.setTime(gBackoffUntil);
  363.         LogWrite("Will retry at " + retry);
  364.     }
  365.     fms.lastError = code;
  366. }
  367.  
  368. function ReturnErrorCode(code) {
  369.     ReturnErrorMsg(code, MapError(code));
  370. }
  371.  
  372. function ReturnSuccess(msgname, args, restoreState) {
  373.     if (args == null) {
  374.         var args = {};
  375.     }
  376.  
  377.     args.status = 0;
  378.     args.msg = Bundle().GetStringFromName(msgname);
  379.  
  380.     SetState(restoreState ? restoreState : "ready");
  381.     ClearBusy();
  382.     gFailureCount = 0;
  383.     fms.lastError = 0;
  384.     LogWrite("Success: " + Bundle().GetStringFromName(msgname));
  385.     Notify(args);
  386. }
  387.  
  388. function SetBusy() {
  389.     if (IsBusy._busy) {
  390.         return false;
  391.     } else {
  392.         IsBusy._busy = true;
  393.         SetState("working");
  394.         return true;
  395.     }
  396. }
  397.  
  398. function ClearBusy() {
  399.     IsBusy._busy = false;
  400. }
  401.  
  402. function IsBusy() {
  403.     return IsBusy._busy;
  404. }
  405. IsBusy._busy = false;
  406.  
  407. function GetState() {
  408.     return GetState._state;
  409. }
  410. GetState._state = "ready";
  411.  
  412. function SetState(newstate) {
  413.     if (newstate == GetState()) {
  414.         return;
  415.     }
  416.  
  417.     var os = Cc["@mozilla.org/observer-service;1"]
  418.     .getService(Ci.nsIObserverService);
  419.  
  420.     os.notifyObservers(null, "foxmarks-statechange", newstate);
  421.  
  422.     GetState._state = newstate;
  423. }
  424.  
  425. // Internal callbacks
  426.  
  427. function LogStart(op, dest) {
  428.     LogWrite("------ Foxmarks/" + FoxmarksVersion() + " (" + 
  429.             fms.getStorageEngine("bookmarks") + ") starting " + op +
  430.             " with " + dest + " ------");
  431. }
  432.  
  433. ///////////////////////////////////////////////////////////////////////////
  434. //
  435. // nsFoxmarksService
  436. //
  437.  
  438. fms = null;  // set during initialization to instance object
  439.  
  440. function nsFoxmarksService() {
  441.     LoadFiles();
  442. }
  443.  
  444. nsFoxmarksService.prototype = {
  445.     /////////////////////////////////////////////////////////////////////////
  446.     // nsIFoxmarksService
  447.  
  448.     timer: null,
  449.     _server: null,
  450.     lastmodified: null,
  451.  
  452.     status: function(syncType) {
  453.         // return if we're currently processing another request
  454.         if (!SetBusy()) {
  455.             return false;
  456.         }
  457.         LogStart("status", gSettings.host);
  458.         if(syncType === undefined)
  459.             syncType = "bookmarks";
  460.  
  461.         this.server.status(syncType, function(status, response){
  462.             if (status) {
  463.                 ReturnErrorCode(status);
  464.             }
  465.             else {
  466.                 ReturnSuccess("msg.accountverified", response);
  467.             }
  468.         });
  469.         return true;
  470.     },
  471.     extstatus: function(syncType) {
  472.         // return if we're currently processing another request
  473.         if (!SetBusy()) {
  474.             return false;
  475.         }
  476.         LogStart("extstatus", gSettings.host);
  477.         if(syncType === undefined)
  478.             syncType = "bookmarks";
  479.  
  480.         this.server.extstatus(syncType, function(status, response){
  481.             if (status) {
  482.                 ReturnErrorCode(status);
  483.             }
  484.             else {
  485.                 ReturnSuccess("msg.accountverified", response);
  486.             }
  487.         });
  488.         return true;
  489.     },
  490.     purgepasswords: function(){
  491.         LogStart("purgepasswords", gSettings.host);
  492.         this.server.purgepasswords(function(status){
  493.             if (!status) {
  494.                 ReturnSuccess("msg.synccompleted");
  495.             }
  496.             else {
  497.                 ReturnErrorCode(status);
  498.             }
  499.         });
  500.         return true;
  501.  
  502.     },
  503.  
  504.     verifypin: function(pin) {
  505.         // return if we're currently processing another request
  506.         if (!SetBusy()) {
  507.             return false;
  508.         }
  509.         LogStart("status", gSettings.host);
  510.  
  511.         this.server.verifypin(pin, function(status, response){
  512.             if (status) {
  513.                 ReturnErrorCode(status);
  514.             }
  515.             else {
  516.                 ReturnSuccess("msg.pinverified", response);
  517.             }
  518.         });
  519.         return true;
  520.     },
  521.  
  522.     synchronize: function(automatic) {
  523.         var prevState = GetState();
  524.  
  525.         // return if we're currently processing another request
  526.         if (!SetBusy()) {
  527.             return false;
  528.         }
  529.  
  530.         LogStart("sync", gSettings.host);
  531.         this.server.manual = automatic === true ? false : true;
  532.         this.server.sync(prevState, function(status){
  533.             if (!status) {
  534.                 ReturnSuccess("msg.synccompleted");
  535.             }
  536.             else {
  537.                 ReturnErrorCode(status);
  538.             }
  539.         });
  540.         return true;
  541.     },
  542.  
  543.     synchronizeInitial: function (remoteIsMaster, doMerge) {
  544.         // return if we're currently processing another request
  545.         if (!SetBusy()) {
  546.             return false;
  547.         }
  548.  
  549.         LogStart("initial sync", gSettings.host);
  550.         this.server.manual = true;
  551.  
  552.         if (doMerge) {
  553.             this.server.merge(!remoteIsMaster, Finished);
  554.         } else {
  555.             if (remoteIsMaster) {
  556.                 this.server.download(Finished);
  557.             } else {
  558.                 this.server.upload(Finished);
  559.             }
  560.         }
  561.  
  562.         return true;
  563.  
  564.         function Finished(status) {
  565.             if (!status) {
  566.                 ReturnSuccess("msg.synccompleted");
  567.             } else {
  568.                 ReturnErrorCode(status);
  569.             }
  570.         }
  571.     },
  572.  
  573.     upload: function () {
  574.         // return if we're currently processing another request
  575.         if (!SetBusy()) {
  576.             return false;
  577.         }
  578.  
  579.         LogStart("upload", gSettings.host);
  580.  
  581.         this.server.manual = true;
  582.         this.server.upload(function(status){
  583.             if (!status) {
  584.                 ReturnSuccess("msg.uploadcompleted");
  585.             }
  586.             else {
  587.                 ReturnErrorCode(status);
  588.             }
  589.         });
  590.         return true;
  591.     },
  592.  
  593.     download: function () {
  594.         // return if we're currently processing another request
  595.         if (!SetBusy()) {
  596.             return false;
  597.         }
  598.  
  599.         LogStart("download", gSettings.host);
  600.  
  601.         this.server.manual = true;
  602.         this.server.download(function(status){
  603.             if (!status) {
  604.                 ReturnSuccess("msg.remotefilecopied");
  605.             }
  606.             else {
  607.                 ReturnErrorCode(status);
  608.             }
  609.         });
  610.         return true;
  611.     },
  612.  
  613.     getProfileNames: function() {
  614.         var prevState = GetState();
  615.  
  616.         var funcFinished = function(status, response) {
  617.             if (!status) {
  618.                 LogWrite("GetProfileNames succeeded; response is " +
  619.                         response.toSource());
  620.                 ReturnSuccess("error.0", response, prevState);
  621.             } else {
  622.                 ReturnErrorMsg(status, MapError(status), prevState);
  623.             }
  624.         };
  625.         // return if we're currently processing another request
  626.         if (!SetBusy()) {
  627.             return false;
  628.         }
  629.  
  630.         LogStart("getProfileNames", gSettings.acctMgrHost);
  631.  
  632.         if (this.server.getProfileNames) {
  633.             this.server.getProfileNames(funcFinished);
  634.             return true;
  635.         } else {
  636.             return false;
  637.         }
  638.  
  639.     },
  640.  
  641.     launchSuccessPage: function(){
  642.         this.server.countItems("bookmarks",
  643.             "bookmark", function(status, num_bookmarks){
  644.             var currver = FoxmarksVersion();
  645.             FoxmarksOpenInNewTab(gSettings.httpProtocol +
  646.                 gSettings.webHost + "/firefox/success/" + currver, 
  647.                 true, FoxmarksBuildPostData(num_bookmarks));
  648.             gSettings.currVersion = currver;
  649.         });
  650.     },
  651.     cancel: function() {
  652.         this.server.cancel();
  653.     },
  654.  
  655.     logWrite: function (msg) {
  656.         if (!logStream)
  657.             logFileOpen();
  658.  
  659.         var d = new Date();
  660.         var year = d.getFullYear();
  661.         var month = d.getMonth() + 1; if (month < 10) month = "0" + month;
  662.         var day = d.getDate(); if (day < 10) day = "0" + day;
  663.         var hour = d.getHours(); if (hour < 10) hour = "0" + hour;
  664.         var minute = d.getMinutes(); if (minute < 10) minute = "0" + minute;
  665.         var sec = d.getSeconds(); if (sec < 10) sec = "0" + sec;
  666.  
  667.         // format is [YYYY-MM-DD HH:MM:SS] msg\n
  668.         var string = "[" + year + "-" + month + "-" + day + " " + hour + ":" +
  669.             minute + ":" + sec + "] " + msg + "\n";
  670.  
  671.         if(gSettings.getDebugOption("dumplog"))
  672.             dump("Foxmarks: " + string + "\n");
  673.         logStream.write(string, string.length)
  674.     },
  675.  
  676.     getState: function() {
  677.         return GetState();
  678.     },
  679.  
  680.     _password: null,
  681.     _pin: null,
  682.  
  683.     getPassword: function() {
  684.         return this._password;
  685.     },
  686.  
  687.     setPassword: function(password) {
  688.         this._password = password;
  689.     },
  690.     getPin: function() {
  691.         return this._pin;
  692.     },
  693.  
  694.     setPin: function(password) {
  695.         this._pin = password;
  696.     },
  697.  
  698.     getStorageEngine: function(synctype) {
  699.         return getDatasourceAttribute(synctype, "engine");
  700.     },
  701.  
  702.     getLastError: function() {
  703.         return this.lastError;
  704.     },
  705.  
  706.     lastError: 0,
  707.     _uninstall: false,
  708.     _channel: null,
  709.     _pingListener: {
  710.          onStartRequest: function () {},
  711.          onDataAvailable: function () {},
  712.          onStopRequest: function () {},
  713.          onChannelRedirect: function (aOldChannel, aNewChannel, aFlags) {
  714.             this._newchannel = aNewChannel;
  715.          },
  716.         getInterface: function (aIID) {
  717.             try {
  718.                 return this.QueryInterface(aIID);
  719.             } catch (e) {
  720.                 throw Components.results.NS_NOINTERFACE;
  721.             }
  722.         },
  723.         onProgress : function (aRequest, aContext, aProgress, aProgressMax) { },
  724.         onStatus : function (aRequest, aContext, aStatus, aStatusArg) { },
  725.         onRedirect : function (aOldChannel, aNewChannel) { },
  726.         QueryInterface : function(aIID) {
  727.             if (aIID.equals(Components.interfaces.nsISupports) ||
  728.                 aIID.equals(Components.interfaces.nsIInterfaceRequestor) ||
  729.                 aIID.equals(Components.interfaces.nsIChannelEventSink) || 
  730.                 aIID.equals(Components.interfaces.nsIProgressEventSink) ||
  731.                 aIID.equals(Components.interfaces.nsIHttpEventSink) ||
  732.                 aIID.equals(Components.interfaces.nsIStreamListener))
  733.             return this;
  734.             throw Components.results.NS_NOINTERFACE;
  735.          } 
  736.      },
  737.      pingServer: function(topic){
  738.         var ioService = Components.classes["@mozilla.org/network/io-service;1"]
  739.             .getService(Components.interfaces.nsIIOService);
  740.  
  741.         // create an nsIURI
  742.         var attrs = [];
  743.         var session = Date.now().toString(36);
  744.         attrs.push("app="       + "jezebel");
  745.         attrs.push("mid="       + gSettings.machineId);
  746.         attrs.push("sess="      + session);
  747.         attrs.push("page="      + topic);
  748.         attrs.push("username="  + gSettings.username);
  749.         attrs.push("no_cache="  + Date.now().toString(36));
  750.  
  751.         var query = attrs.join("&");
  752.         var url = gSettings.httpProtocol + "tr.foxmarks.com/tracking/impressions.gif?" + query;
  753.         this._pingRequest = new Request("GET",url, null, false,true);  
  754.         this._pingRequest.Start(function(){});
  755.     },
  756.     getLastModified: function(synctype) {
  757.         return fms.lastmodified;
  758.     },
  759.  
  760.     get server() {
  761.         if (!this._server) {
  762.             this._server = new SyncServer();
  763.         }
  764.         return this._server;
  765.     },
  766.  
  767.     /////////////////////////////////////////////////////////////////////////
  768.     //
  769.     // nsIObserver
  770.  
  771.     observe: function(subject, topic, data)  { // called at startup
  772.         var timerCallback = {
  773.  
  774.             notify: function(timer) {
  775.  
  776.                 var now = new Date().getTime();
  777.  
  778.                 // scan entire bookmark set to find
  779.                 // last modified date for entire set
  780.                 // XXX: Implement
  781.  
  782.                 // Do update nag if necessary
  783.                 if (gSettings.daysSinceLastUpdateNag > 28 &&
  784.                     FoxmarksUpdateAvailable()) {
  785.                     gSettings.lastNagDate = gSettings.NowAsGMT;
  786.                     UpdateNag();
  787.                 }
  788.  
  789.                 // Do automatic sync if necessary
  790.                 if (!IsBusy() && (!gFailureCount || now > gBackoffUntil) &&
  791.                     gSettings.synchOnTimer && gSettings.haveSynced) {
  792.                     if (gSettings.minutesSinceLastSync > 
  793.                             gSettings.autoSynchFreq) {
  794.                         fms.synchronize(true);
  795.                     } else {
  796.                         if (fms.lastmodified && gSettings.haveSynced && 
  797.                             fms.lastmodified >
  798.                             Date.parse(gSettings.lastSynchDate) &&
  799.                             now - fms.lastmodified > 5 * 60 * 1000) {
  800.                             fms.synchronize(true);
  801.                         }
  802.                     }
  803.                 }
  804.             }
  805.         }
  806.  
  807.         if (topic == "app-startup") {
  808.             // Pre-initialization here.
  809.             var os = Cc["@mozilla.org/observer-service;1"].
  810.                 getService(Ci.nsIObserverService);
  811.             os.addObserver(this, "quit-application-requested", false);
  812.             os.addObserver(this, "foxmarks-datasourcechanged", false);
  813.             os.addObserver(this, "foxmarks-rununittest", false);
  814.             os.addObserver(this, "foxmarks-unittesterror", false);
  815.             os.addObserver(this, "earlyformsubmit", false);
  816.             os.addObserver(this, "final-ui-startup", false);
  817.             os.addObserver(this, "em-action-requested", false);
  818.             os.addObserver(this, "quit-application-granted", false);
  819.             os.addObserver(this, "foxmarks-showsettingpane", false);
  820.         } else if (topic == "final-ui-startup") {
  821.             // Real initialization starts here.
  822.             fms = this;
  823.             var dsList = loadDatasourceSet(true); 
  824.  
  825.             fms.nat = {}; 
  826.             for(var x = 0; x < dsList.length; x++)
  827.                 fms.nat[dsList[x].syncType] = dsList[x].WatchForChanges(this.server);
  828.  
  829.             if (!gSettings.wizardSuppress && !gSettings.useOwnServer &&
  830.                     !gSettings.haveSynced) {
  831.                 FoxmarksLaunchSetupWizard();
  832.             } else if (gSettings.majorVersion < 2) {
  833.                 gSettings.majorVersion = 2;
  834.                 if (gSettings.hostname == "sync.foxmarks.com" || 
  835.                         gSettings.hostname == "sync.foxcloud.com")  {
  836.                     // Standard upgrade
  837.                     FoxmarksLaunchSetupWizard();
  838.                 } else {
  839.                     // Custom server upgrade
  840.                     var sb = Bundle().GetStringFromName;
  841.                     Cc["@mozilla.org/embedcomp/prompt-service;1"].
  842.                         getService(Ci.nsIPromptService).
  843.                         alert(null, sb("appname.long"), 
  844.                         sb("msg.upgrade2custom"));
  845.                     gSettings.useOwnServer = true;
  846.                     gSettings.url = gSettings.serverType + "://" + 
  847.                         gSettings.hostname + 
  848.                         gSettings.path.replace("{username}", 
  849.                             gSettings.username) +
  850.                         "foxmarks.json";
  851.                 }
  852.             } else {
  853.                 // need to check for upgrades here
  854.                 var currver = FoxmarksVersion();
  855.                 var lastver = gSettings.currVersion;
  856.                 var ca = currver.split(".");
  857.                 var la = lastver.split(".");
  858.                 var newver =false;
  859.                 
  860.                 if(ca.length != la.length){
  861.                     newver = true;
  862.                 } else {
  863.                     for(var x=0; x < ca.length-1; x++){
  864.                         if(parseInt(ca[x]) != parseInt(la[x])){
  865.                             newver = true;
  866.                             break;
  867.                         }
  868.                     }
  869.                 }
  870.                 if(newver){
  871.                     FoxmarksLaunchUpgradePage();
  872.                 }
  873.             }
  874.  
  875.             this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
  876.             this.timer.initWithCallback(timerCallback, 1000*60,
  877.                 Ci.nsITimer.TYPE_REPEATING_SLACK);
  878.         } else if (topic == "quit-application-requested") {
  879.             HandleShutdown();
  880.         } else if(topic == "em-action-requested"){
  881.             subject.QueryInterface(Components.interfaces.nsIUpdateItem);
  882.             if(subject.id == "foxmarks@kei.com"){
  883.                 switch(data){
  884.                     case 'item-uninstalled':
  885.                         this._uninstall = true;
  886.                         this.pingServer("uninstall");
  887.                         break;
  888.                     case 'item-disabled':
  889.                         this.pingServer("disable");
  890.                         break;
  891.                     case 'item-cancel-action':
  892.                         if(this._uninstall == false){
  893.                             this.pingServer("cancel-disable");
  894.                         } else {
  895.                             this.pingServer("cancel-uninstall");
  896.                         }
  897.                         this._uninstall = false;
  898.                         break;
  899.                 }
  900.  
  901.             }
  902.  
  903.         } else if(topic == "quit-application-granted"){
  904.             if(this._uninstall){
  905.                 gSettings.clearAllPrefs();
  906.             }
  907.         } else if(topic == "foxmarks-showsettingpane"){
  908.             if(data.length > 0){
  909.                 OpenFoxmarksSettingsDialog(data);
  910.             }
  911.         } else if (topic == "foxmarks-unittesterror"){
  912.             try {
  913.                 ReturnErrorCode(parseInt(data));
  914.             }
  915.             catch(e){
  916.                 Components.utils.reportError(e);
  917.             }
  918.         } else if (topic == "foxmarks-rununittest"){
  919.             this.server.runUnitTest();
  920.         } else if (topic == "foxmarks-datasourcechanged") {
  921.             var a = data.split(';');
  922.             this.lastmodified = parseInt(a[0]);
  923.             var okState = (GetState() == "ready" || GetState() == "unknown");
  924.             if (okState && gSettings.haveSynced &&
  925.                 gSettings.isSyncEnabled(a[1]) && 
  926.                 this.lastmodified > Date.parse(gSettings.lastSynchDate)) {
  927.                 SetState("dirty");
  928.             }
  929.         } else {
  930.             LogWrite("Yikes unknown topic " + topic);
  931.         }
  932.     },
  933.  
  934.     /////////////////////////////////////////////////////////////////////////
  935.     // nsIFormSubmitObserver
  936.     notify : function (formElement, aWindow, actionURI) {
  937.         if(fms.nat["passwords"])
  938.             fms.nat["passwords"].formsubmit(formElement);
  939.         return true;
  940.     },
  941.  
  942.     /////////////////////////////////////////////////////////////////////////
  943.     // nsIClassInfo
  944.     getInterfaces: function (aCount) {
  945.         var interfaces = [Ci.nsIFoxmarksService,
  946.         Ci.nsIObserver,
  947.         Ci.nsIFormSubmitObserver,
  948.         Ci.nsiRDFObserver];
  949.         aCount.value = interfaces.length;
  950.         return interfaces;
  951.     },
  952.  
  953.     getHelperForLanguage: function (aLanguage) {
  954.         return null;
  955.     },
  956.  
  957.     get contractID() {
  958.         return "@foxcloud.com/extensions/foxmarks;1";
  959.     },
  960.  
  961.     get classDescription() {
  962.         return "Foxmarks Service";
  963.     },
  964.  
  965.     get classID() {
  966.         return Components.ID("{49ace257-6111-48b2-a988-f9eb38b0fa58}");
  967.     },
  968.  
  969.     get implementationLanguage() {
  970.         return Ci.nsIProgrammingLanguage.JAVASCRIPT;
  971.     },
  972.  
  973.     get flags() {
  974.         return Ci.nsIClassInfo.SINGLETON;
  975.     },
  976.  
  977.     /////////////////////////////////////////////////////////////////////////
  978.     // nsISupports
  979.     QueryInterface: function (aIID) {
  980.         if (!aIID.equals(Ci.nsIFoxmarksService) &&
  981.             !aIID.equals(Ci.nsISupports) &&
  982.             !aIID.equals(Ci.nsIRDFObserver) &&
  983.             !aIID.equals(Ci.nsIFormSubmitObserver) &&
  984.             !aIID.equals(Ci.nsIObserver))
  985.         throw Components.results.NS_ERROR_NO_INTERFACE;
  986.         return this;
  987.     }
  988. };
  989.  
  990. var gModule = {
  991.     _firstTime: true,
  992.  
  993.     registerSelf: function (aComponentManager, aFileSpec, aLocation, aType) {
  994.         if (this._firstTime) {
  995.             this._firstTime = false;
  996.             throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
  997.         }
  998.  
  999.  
  1000.         aComponentManager = aComponentManager.
  1001.         QueryInterface(Ci.nsIComponentRegistrar);
  1002.  
  1003.         for (var key in this._objects) {
  1004.             var obj = this._objects[key];
  1005.             aComponentManager.registerFactoryLocation(obj.CID, obj.className,
  1006.                 obj.contractID, aFileSpec, aLocation, aType);
  1007.         }
  1008.  
  1009.         // Make the Foxmarks Service a startup observer
  1010.         var cm = Cc["@mozilla.org/categorymanager;1"].
  1011.         getService(Ci.nsICategoryManager);
  1012.         cm.addCategoryEntry("app-startup", this._objects.service.className,
  1013.             "service," + this._objects.service.contractID, true, true, null);
  1014.     },
  1015.  
  1016.  
  1017.     getClassObject: function (aComponentManager, aCID, aIID) {
  1018.         if (!aIID.equals(Ci.nsIFactory))
  1019.             throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  1020.  
  1021.         for (var key in this._objects) {
  1022.             if (aCID.equals(this._objects[key].CID))
  1023.                 return this._objects[key].factory;
  1024.         }
  1025.  
  1026.         throw Components.results.NS_ERROR_NO_INTERFACE;
  1027.     },
  1028.  
  1029.     _makeFactory: #1= function(ctor) {
  1030.         return {
  1031.             createInstance: function (outer, iid) {
  1032.                 if (outer != null)
  1033.                     throw Components.results.NS_ERROR_NO_AGGREGATION;
  1034.                 return (new ctor()).QueryInterface(iid);
  1035.             }
  1036.         };
  1037.     },
  1038.  
  1039.     _objects: {
  1040.         service: { CID : nsFoxmarksService.prototype.classID,
  1041.             contractID : nsFoxmarksService.prototype.contractID,
  1042.             className  : nsFoxmarksService.prototype.classDescription,
  1043.             factory    : #1#(nsFoxmarksService)
  1044.         }
  1045.     },
  1046.  
  1047.     canUnload: function (aComponentManager)
  1048.     {
  1049.         return true;
  1050.     }
  1051. };
  1052.  
  1053. function NSGetModule(compMgr, fileSpec)
  1054. {
  1055.     return gModule;
  1056. }
  1057.